#ifndef C3DISPLAYOBJECT_H
#define C3DISPLAYOBJECT_H

#include "c3Engine.h"
#include "c3Events.h"
#include "c3Geo.h"

class Mode;

struct ObjectFlags { enum {
  None = 0,
  Interactive = 1<<0,
};};

struct Align { enum
{
  Left = 0,
  Center,
  Right,

	NoAlign,

  Top = Left,
  Bottom = Right,
};};

struct DisplayObjectEvent { enum
{
  None = 'EDPO',
  ObjectDied,
};};

class DisplayObject
{
public:
  uint8 Alpha;
  uint Rotation; /// 0 - 4095
  uint Color;
  int X, Y;
  int Width, Height;
	float ScaleX, ScaleY;
	Blend_Mode BlendMode;
  bool Visible;
  bool Enabled, MouseChildren;

  int NextThink;

  struct __Effects
  {
    struct GlowEffect
    {
      bool Enabled;
      uint Color;
      GlowEffect() { Enabled = false; Color = 0xffffffff; }
    } Glow;
    struct ColorFilterEffect
    {
      bool Enabled;
      uint Color;
      uint Mode;

      ColorFilterEffect() { Enabled = false; Color = 0xffffffff; Mode = Multiply; }

      enum
      {
        Multiply,
        Add
      };
    } ColorFilter;
  } Effects;

  struct _Animate
  {
    void FadeIn(int speed = 15);
    void FadeOut(int speed = 15);

		void ShowAfter(int frames);
		void HideAfter(int frames);

		void SlideFrom(int x, int y, int intime = 60, int delay = 0);

		void Pulse(int inTime = 60, float scaleX = 0.2f, float scaleY = 0.2f);

		void Bounce(int inTime = 60, int height = 50);

    /// Ignore this plox! Please please PLEASE, don't change this under
    /// ANY circumstances!
    DisplayObject* mParent;
  } Animate;

  c3Rectangle InputMaskRect;
  bool InputMask;
  /// TODO: do this
  // Rectangle DrawMaskRect;
  // bool DrawMask;

  bool Zombie;

  bool Is(uint type) { return type == Type(); }
  virtual uint Type() const { return 'dpob'; }

  unsigned long long int Userdata[4];
  //int MouseX, MouseY;

	/// Display
  Image* TheImage;

	virtual void Show() { Visible = true; Enabled = true; }
	virtual void Hide() { Visible = false; Enabled = false; }

  /// parent relations
  DisplayObject* GetParent() const { return mParent; }
  
  /// children
  DisplayObject* GetChildAt(uint i) const { return mChildren[i]; }
  int GetChildIndex(const DisplayObject* obj) const;
  DisplayObject* GetChildAtPoint(int x, int y, bool onlyEnabled = false);
  int AddChild(DisplayObject* obj);
  int AddChild(DisplayObject& obj);
  DisplayObject* RemoveChildAt(uint at);
  DisplayObject* RemoveChild(DisplayObject* obj);
  template <typename Predicate>
  void ForEachChild(Predicate predicate)
  {
    for_each(mChildren.begin(), mChildren.end(), predicate);
  }
  size_t GetNumChildren() const { return mChildren.size(); }

  int AddToParent(DisplayObject& obj);
  int AddToParent(DisplayObject* obj);

  /// events
  virtual void AddEvent(int event, DisplayObject* control = 0);
  void SetEventBuffer(EventBuffer* events) { mEvents = events; }
	EventBuffer* GetEventBuffer() const { return mEvents; }

  /// position and size
  void AlignToParent(int horizontal, int vertical = Align::NoAlign);
	void AlignChildren(int horizontal, int vertical = Align::NoAlign);
  bool IsPointInside(int x, int y, bool applyInputMask = false) const;
  bool IsOverlapping(const DisplayObject& other) const;
  c3Rectangle GetRect() const;
  void SetDimsFromImage(Image* fromImage = 0);
	void SetSize(int width, int height);

	void SetSizeFromChildren(bool moveWithExpansion = true);

	void SetAbsolutePosition(int x, int y);
	void SetPosition(int x, int y);

	void MoveBy(int x, int y);

  c3Point LocalToGlobal(int x, int y);

  /// misc
  uint GetFlags() const { return mFlags; }

  DisplayObject();
  virtual ~DisplayObject();

  uint Update(int dt);
  uint Draw(int x = 0, int y = 0, uint8 alpha = 255);

	void* operator new(size_t sz)
	{
		mDynamicFlag = true;
		return ::operator new(sz);
	}
	bool SetDynamic(bool d) { bool ret = mIsDynamic; mIsDynamic = d; return ret; }
protected:
	static bool mDynamicFlag;
  void Attached(DisplayObject* parent);
  void Unattached();

	bool mIsDynamic;
  vector<DisplayObject*> mChildren;
  DisplayObject* mParent;
  uint mFlags;

  EventBuffer* mEvents;

  struct AnimationStruct
  {
		struct Animation
		{
			bool mEnabled;
			Animation() : mEnabled(false) {}
			void Animate(int dt, DisplayObject* on)
			{
				if (mEnabled) Do(dt, on);
			}
			virtual void Do(int dt, DisplayObject* on) = 0;
		};
		struct AlphaFadeStruct : public Animation
    {
      int mDestAlpha;
      int mFadeSpeed;
      virtual void Do(int dt, DisplayObject* on);
    } AlphaFade;

		struct SlideStruct : public Animation
		{
			int mFromX, mFromY, mToX, mToY;
			int mTime, mEndTime;
			virtual void Do(int dt, DisplayObject* on);
		} Slide;

		struct PulseStruct : public Animation
		{
			float mScaleX, mScaleY;
			int mSpeed, mTime;
			virtual void Do(int dt, DisplayObject* on);
		} Pulse;

		struct BounceStruct : public Animation
		{
			int mInTime, mTime;
			int mHeight;
			int mBaseY;
			virtual void Do(int dt, DisplayObject* on);
		} Bounce;

		struct ShowAfterStruct : public Animation
		{
			bool mShow;
			int mTimeLeft;
			virtual void Do(int dt, DisplayObject* on);
		} ShowAfter;

    void Do(int dt, DisplayObject* on)
    {
      AlphaFade.Animate(dt, on);
			Slide.Animate(dt, on);
			Pulse.Animate(dt,on);
			ShowAfter.Animate(dt,on);
			Bounce.Animate(dt,on);
    }
  } mAnimation;

  virtual void Think() {};
  virtual void UpdateAt(int dt);
  virtual uint DrawAt(int x, int y, uint8 alpha);
	void DestroyChildren();
private:

};

#endif